package com.qf.leadnewsschedule.service;

import com.qf.leadnewsmodel.consts.TaskConst;
import com.qf.leadnewsmodel.enums.TaskTypeEnum;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;

import java.util.HashSet;
import java.util.Set;

@Component
public class RedisService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public void save2List(String key, String value) {
        redisTemplate.opsForList().leftPush(key,value);
    }

    public void save2ZSet(String key, long score, String value) {
        redisTemplate.opsForZSet().add(key,value,score);
    }

    public String lRightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    public Set<String> scan(String partten,Integer count){

        Set<String> keys = new HashSet<>();

        try(RedisConnection conn = redisTemplate.getConnectionFactory().getConnection()){
             /*
            这里我们即使指定检索10000个key，scan不是一次性将10000全部查出来
            会每次查出一部分，根据游标，进行下一次的查询，最后返回的是10000个
            所以这个指令不会像keys指令那样，对redis服务造成阻塞

            keys -- 线上会被禁用
            scan -- 替代keys的方案
         */
            ScanOptions ops = ScanOptions
                    .scanOptions()
                    .match(partten) //根据什么样的匹配规则进行检索
                    .count(count) //检索多少个
                    .build();
            Cursor<byte[]> cursor = conn.scan(ops);
            while (cursor.hasNext()){
                byte[] bytes = cursor.next();
                keys.add(new String(bytes));
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        return keys;
    }

    /**
     * 根据key，查询符合指定范围的value
     * @param key
     * @param minScore
     * @param maxScore
     * @return
     */
    public Set<String> zRangeByScore(String key,long minScore, long maxScore){
        Set<String> values = redisTemplate.opsForZSet().rangeByScore(key, minScore, maxScore);
        return values;
    }

    /***
     * 根据key，移除指定的values值
     * @param key
     * @param values
     */
    public void zsetRemove(String key,String ... values){
        redisTemplate.opsForZSet().remove(key,values);
    }

    /**
     * 批量向redis中的List结构添加指定key和对应values数据
     * @param key
     * @param values
     */
    public void lLeftPush(String key,String ... values){
        redisTemplate.opsForList().leftPushAll(key,values);
    }

    public void pipelineRefresh(String futrueKey,Set<String> values){
        redisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {

                //移除zset中的预热数据
                redisTemplate.opsForZSet().remove(futrueKey,values.toArray(new String[0]));

                //添加到list中
                String suffix = futrueKey.split(":")[2];
                String nowKey = TaskConst.NOW_CONSUME_PREFIX + suffix;
                redisTemplate.opsForList().leftPushAll(nowKey,values.toArray(new String[0]));

                return null;
            }
        });
    }

    public void clearDataByKey(){
        TaskTypeEnum[] values = TaskTypeEnum.values();
        for (TaskTypeEnum typeEnum : values) {
            int taskType = typeEnum.getTaskType();
            int priority = typeEnum.getPriority();

            String futrueKey = TaskConst.FUTRUE_CONSUME_PREFIX + buildSuffix(taskType,priority);
            String nowKey = TaskConst.NOW_CONSUME_PREFIX + buildSuffix(taskType,priority);
            redisTemplate.delete(futrueKey);
            redisTemplate.delete(nowKey);
        }
    }

    private String buildSuffix(int taskType, int priority){
        return taskType + "_" + priority;
    }

    @Autowired
    private RedissonClient client;

    /**
     * 获取写锁
     * @param lockName
     * @return
     */
    public RLock getWriteLock(String lockName) {
        RReadWriteLock readWriteLock = client.getReadWriteLock(lockName);
        return readWriteLock.writeLock();
    }

    /**
     * 获取读锁
     * @param lockName
     * @return
     */
    public RLock getReadLock(String lockName) {
        RReadWriteLock readWriteLock = client.getReadWriteLock(lockName);
        return readWriteLock.readLock();
    }
}
